/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*********************************************************************************************
    *   Filename        : power_hw.c

    *   Description     :

    *   Author          : Bingquan

    *   Email           : caibingquan@zh-jieli.com

    *   Last modifiled  : 2020-06-05 09:37

    *   Copyright:(c)JIELI  2011-2019  @ , All Rights Reserved.
*********************************************************************************************/
#include "power_hw.h"
#include "asm/cpu.h"
#include "asm/power_interface.h"
#include "power_wakeup.h"
#include "lrc_hw.h"
#include "lp_timer_hw.h"
#include "gpio.h"
#include "typedef.h"
#include "asm/charge.h"
#include "asm/pwm_led.h"
#include "asm/usb.h"
#include "asm/wdt.h"
#include "asm/adc_api.h"
#include "asm/power/power_port.h"

#define LOG_TAG_CONST       PMU
#define LOG_TAG             "[PMU]"
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"

#define OS_ENTER_CRITICAL()  \
    CPU_CRITICAL_ENTER(); \

#define OS_EXIT_CRITICAL()  \
    CPU_CRITICAL_EXIT()

extern u8 is_lcd_on(void);

#define nv_memory_malloc(a)     malloc(a)
#define nv_memory_free(a)       free(a)

/**
 * @brief Low Power Debug Signal(mapping to DSview)
 */
#define DEBUG_SINGAL_GROUP1_ERR()           // IO_DEBUG_TOGGLE(A, 14)
#define DEBUG_SINGAL_GROUP0_ERR(x)          // IO_DEBUG_TOGGLE(A, 13)
#define DEBUG_SINGAL_PLL_DELAY(x)           // if (x) IO_DEBUG_1(A, 8) else IO_DEBUG_0(A, 8)
#define DEBUG_SINGAL_LP(x)                  // if (x) IO_DEBUG_1(A, 7) else IO_DEBUG_0(A, 7)
#define DEBUG_SINGAL_LP_TIMER(x)            // if (x) IO_DEBUG_1(A, 6) else IO_DEBUG_0(A, 6)
#define DEBUG_SINGAL_WAIT(x)                // IO_DEBUG_TOGGLE(A, 8)
#define DEBUG_SINGAL_OSC_SWITCH(x)          // IO_DEBUG_TOGGLE(A, 4)

#define     LP_IO_DEBUG_0(i, x)       // {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT &= ~BIT(x);}
#define     LP_IO_DEBUG_1(i, x)       // {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT |= BIT(x);}

/* ----------------------------------------------------------- */

/********************************************************************************/
/*
 *          --------低功耗模式配置
 *
 *                  P.S 仅内部开发人员配置
 */

#define SLEEP_TIMEOUT_MIN_US                (SLEEP_SAVE_TIME_US + __this->recover_time_us)

#define HW_RESERVE_TIME_US  (TSTB0 + TSTB1 + TSTB2 + TSTB3 + TSTB4 + TSTB5 + TSTB6)

#define RESET_RESERVE_TIME_US   (1500L)        // extern wakeup reserve time
#define CPU_RESERVE_TIME_US     (1500L)        // extern wakeup reserve time
#define RESET_HW_TIME_US        (__tus_cnt((1 << __this->hw.kstb4) + (1 << __this->hw.kstb5) + (1 << __this->hw.kstb6)))

/*
 * VDDIOM & VDDIOW 对应关系，保护VDDIOM > VDDIOW 1 level
 */
static const int vddiow2m_table[] = {
    VDDIOM_VOL_22V, // VDDIOW_VOL_21V
    VDDIOM_VOL_26V, // VDDIOW_VOL_24V
    VDDIOM_VOL_30V, // VDDIOW_VOL_28V
    VDDIOM_VOL_36V, // VDDIOW_VOL_32V
};

static const int vddiom2w_table[] = {
    VDDIOW_VOL_21V, // VDDIOM_VOL_20V
    VDDIOW_VOL_21V, // VDDIOM_VOL_22V
    VDDIOW_VOL_21V, // VDDIOM_VOL_24V
    VDDIOW_VOL_24V, // VDDIOM_VOL_26V
    VDDIOW_VOL_24V, // VDDIOM_VOL_28V
    VDDIOW_VOL_28V, // VDDIOM_VOL_30V
    VDDIOW_VOL_28V, // VDDIOM_VOL_32V
    VDDIOW_VOL_32V, // VDDIOM_VOL_36V
};

#define SOFTOFF_KEEP_LRC 1

struct power_hdl low_power_hdl SEC(.non_volatile_ram);

#define __this (&low_power_hdl)

static u8 vdc13_lel_bak;
static u32 sys_div_bak;

static void P33_PORT_DUMP(void)
{
    log_info("P3_PORT_SEL10 %x",    p33_rx_1byte(P3_PORT_SEL10));
    log_info("P3_PORT_SEL11 %x",    p33_rx_1byte(P3_PORT_SEL11));
    log_info("P3_PORT_SEL12 %x",    p33_rx_1byte(P3_PORT_SEL12));
    log_info("P3_PORT_SEL13 %x",    p33_rx_1byte(P3_PORT_SEL13));
    log_info("P3_PORT_SEL14 %x",    p33_rx_1byte(P3_PORT_SEL14));
    log_info("P3_PORT_SEL15 %x",    p33_rx_1byte(P3_PORT_SEL15));
    log_info("P3_PORT_SEL16 %x",    p33_rx_1byte(P3_PORT_SEL16));
    log_info("P3_PORT_SEL17 %x",    p33_rx_1byte(P3_PORT_SEL17));
    log_info("P3_PINR_CON   %x",    p33_rx_1byte(P3_PINR_CON));
}

void P33_ANA_DUMP(void)
{
    log_info("P3_ANA_CON2 %x",    p33_rx_1byte(P3_ANA_CON2));
    log_info("P3_ANA_CON6 %x",    p33_rx_1byte(P3_ANA_CON6));
    log_info("P3_ANA_CON9 %x",    p33_rx_1byte(P3_ANA_CON9));
}

static void __hw_power_dump(void)
{
    log_info("P3_PMU_CON0 0x%x", P33_CON_GET(P3_PMU_CON0));
    log_info("P3_PMU_CON1 0x%x", P33_CON_GET(P3_PMU_CON1));
    log_info("P3_PMU_CON2 0x%x", P33_CON_GET(P3_PMU_CON2));
    log_info("P3_PMU_CON3 0x%x", P33_CON_GET(P3_PMU_CON3));
    log_info("P3_PMU_CON4 0x%x", P33_CON_GET(P3_PMU_CON4));
    log_info("P3_PMU_CON5 0x%x", P33_CON_GET(P3_PMU_CON5));
    log_info("P3_LP_TMR0_CLK 0x%x", P33_CON_GET(P3_LP_TMR0_CLK));

    log_info("P3_LP_PRP0 0x%x", P33_CON_GET(P3_LP_PRP0));
    log_info("P3_LP_PRP1 0x%x", P33_CON_GET(P3_LP_PRP1));

    log_info("P3_LP_STB0_STB1 0x%x", P33_CON_GET(P3_LP_STB0_STB1));
    log_info("P3_LP_STB2_STB3 0x%x", P33_CON_GET(P3_LP_STB2_STB3));
    log_info("P3_LP_STB4_STB5 0x%x", P33_CON_GET(P3_LP_STB4_STB5));
    log_info("P3_LP_STB6      0x%x", P33_CON_GET(P3_LP_STB6));

    log_info("P3_LP_TMR0_CON 0x%x", P33_CON_GET(P3_LP_TMR0_CON));
    log_info("P3_LP_TMR1_CON 0x%x", P33_CON_GET(P3_LP_TMR1_CON));
}

extern bool charge_check_wakeup_is_set_ok(void);

static void low_power_change_osc_type(void);
/*
 *                   HW Sfr Layer
 */
__attribute__((always_inline)) static void ndelay(u32 d)
{
    while (d--) {
        asm volatile("nop");
    }
}

AT_VOLATILE_RAM_CODE
static void delay_nus(u32 n)
{
    ndelay(n * __this->hw.delay_usec);
}

static void udelay(u32 usec)
{
    JL_TIMER0->CON = BIT(14);
    JL_TIMER0->CNT = 0;
    JL_TIMER0->PRD = 16 * 1000000L / 1000000L  * usec; // 1us
    JL_TIMER0->CON = BIT(0); // sys clk
    while ((JL_TIMER0->CON & BIT(15)) == 0);
    JL_TIMER0->CON = BIT(14);
}

/***********************************************************/
/*
 *
 * bb
 *      _________                         ___________
 *               |_______________________|
 * cpu           Tprp                 Tcke
 *      ____________                   ______________
 *                  |_________________|
 *
 */
/***********************************************************/
// RTC OSC sel 32K
// BT OSC sel 12M / prescale 187500
#define TICK_PER_SEC        (__this->hw.osc_hz)

u32 __tcnt_us_lrc(u32 x)
{
    u64 y, r;

    y = (u64)x * (__get_lrc_hz());
    r = y % 1000000L;
    y = y / 1000000L;

    /* remainder */
    if (r) {
        y += 1;
    }
    return (u32)y;
}
/* static  */
u32 __tcnt_us(u32 x)
{
    u64 y, r;

    y = (u64)x * (__this->hw.osc_hz);
    r = y % 1000000L;
    y = y / 1000000L;

    /* remainder */
    if (r) {
        y += 1;
    }
    return (u32)y;
}

u32 __tus_carry(u32 x)
{
    u64 y, z;

    y = (u64)x * (__this->hw.osc_hz);
    y = y / 1000000L;

    z = ((u64)y) * 1000000L;
    z = z / (__this->hw.osc_hz);

    y = x - z;
    /* remainder */
    return (u32)y;
}

u32 __tcnt_us_no_carry(u32 x)
{
    u64 y;

    y = (u64)x * (__this->hw.osc_hz);
    y = y / 1000000L;

    /* remainder */
    return (u32)y;
}

/* static  */
u32 __tcnt_ms(u32 x)
{
    u64 y;

    y = ((u64)x) * (__this->hw.osc_hz);
    y = y / 1000L;

    return (u32)y;
}

/* static  */
u32 __tus_cnt(u32 x)
{
    u64 y;
    y = ((u64)x) * 1000000L;
    y = y / (__this->hw.osc_hz);

    return (u32)y;
}

/* ----------------------------------------------------------- */

/*
 *                   HW Analog
 */
static void __btosc_disable_sw(u8 sw)
{
    if (sw) {
        if (__this->hw.osc_type != OSC_TYPE_BT_OSC) {
            D2SH_EN_SW(0);
        } else {
            D2SH_EN_SW(1);
        }
    } else {
        D2SH_EN_SW(!__this->hw.btosc_disable);
    }
}

static void __hw_set_osc_hz(u32 fre, u8 reset)
{
    u16 addr;

    __this->hw.osc_hz = fre;

    __this->hw.kstb0 = KSTB0(fre);
    __this->hw.kstb1 = KSTB1(fre);
    __this->hw.kstb2 = KSTB2(fre);
    __this->hw.kstb3 = KSTB3(fre);
    __this->hw.kstb4 = KSTB4(fre);
    __this->hw.kstb5 = KSTB5(fre);
    __this->hw.kstb6 = KSTB6(fre);

    if (reset) {
        addr = P3_LP_STB0_STB1;

        p33_cs_h(addr);
        p33_buf((u8)((addr & 0x300) >> 8));               // rd    //adr 3
        p33_buf((u8)(addr & 0xff));       // wr    //adr 3
        p33_buf(PD_STB0_STB1_INIT(__this->hw.kstb1, __this->hw.kstb0));
        p33_buf(PD_STB2_STB3_INIT(__this->hw.kstb3, __this->hw.kstb2));
        p33_buf(PD_STB4_STB5_INIT(__this->hw.kstb5, __this->hw.kstb4));
        p33_buf(PD_STB6_INIT(__this->hw.kstb6));
        p33_cs_l;
    }
}

/* ----------------------------------------------------------- */

/*
 *  -------IC 维护代码 ----------
 *
 *  --电源模拟配置
 */
#include "pmu_analog.c"

/* ----------------------------------------------------------- */

static u32 low_power_group_query(struct list_head *request_head)
{
    u32 usec;
    u32 timeout = -1;
    struct power *p;

    if (list_empty(request_head)) {
        return -1;
    }

    list_for_each_entry(p, request_head, entry) {
        if (p->ops && p->ops->get_timeout) {
            usec = p->ops->get_timeout(p->priv);
        } else {
            usec = -1;
        }
        if (usec == 0) {
            return 0;
        }
        if (timeout > usec) {
            timeout = usec;
        }
    }

    if (timeout == -1) {
        return 0;
    }

    return timeout;
}


/* ----------------------------------------------------------- */

AT_VOLATILE_RAM_CODE
__attribute__((weak)) void __lvd_irq_handler(void)
{
    // Garentee Flash power drop below 0.4V
    spi_flash_port_unmount();

    spi_flash_power_release();

    p33_soft_reset();
}

void lvd_enable(void)
{
    VLVD_RST_EN(0);

    VLVD_WKUP_EN(1);

    VLVD_PND_CLR(1);
}

/* ----------------------------------------------------------- */

/*
*                   HW API Layer
*/
static char *vddiow_string[] = {
    "VDDIOW_VOL_21V",
    "VDDIOW_VOL_24V",
    "VDDIOW_VOL_28V",
    "VDDIOW_VOL_32V",
};

static char *vddiom_string[] = {
    "VDDIOM_VOL_20V",
    "VDDIOM_VOL_22V",
    "VDDIOM_VOL_24V",
    "VDDIOM_VOL_26V",
    "VDDIOM_VOL_28V",
    "VDDIOM_VOL_30V",
    "VDDIOM_VOL_32V",
    "VDDIOM_VOL_36V",
};

static void __hw_power_init(const struct low_power_param *param)
{
    u16 addr;

    __this->hw.osc_type = param->osc_type;

    __this->hw.last_osc_type = __this->hw.osc_type;

    __this->hw.btosc_hz = param->btosc_hz;

    __this->hw.dcdc_port = param->dcdc_port;

    __hw_set_osc_hz((param->osc_type == OSC_TYPE_LRC) ? LRC_Hz_DEFAULT : param->btosc_hz, 1);

    log_info("SPI boot port : 0x%x", spi_get_port());

    log_info("POWER_OSC_INFO: %s - %08d", (__this->hw.osc_type == OSC_TYPE_BT_OSC) ? "BT OSC" : "LRC",
        __this->hw.osc_hz);

    if (__this->hw.osc_type == OSC_TYPE_BT_OSC) {
        __this->hw.osc_hz >>= 6;   // default div 64
    }

    __this->hw.delay_usec = param->delay_us;
    log_info("Delay n * uS: %08d / %08d", param->delay_us, __this->hw.delay_usec);

    __this->hw.btosc_disable = param->btosc_disable;

    p33_tx_1byte(P3_PMU_CON0, PD_CON0_INIT);

    SFR(JL_P33->PMU_CON, 8, 1, 0);
    // LPM Sel
    SFR(JL_P33->PMU_CON, 13, 1, 1);
    // BTOSC Fast up
    SFR(JL_P33->PMU_CON, 12, 1, 0);

    SFR(JL_P33->PMU_CON, 10, 1, 1);
    SFR(JL_P33->PMU_CON, 15, 1, 1);

    switch (__this->hw.osc_type) {
        case OSC_TYPE_BT_OSC:
            __this->recover_time_us = BT_SLEEP_RECOVER_TIME_US;
            p33_tx_1byte(P3_PMU_CON1, PD_CON1_INIT_BT);
            p33_tx_1byte(P3_LP_TMR0_CLK, PD_CON1_INIT_BT);
            break;
        case OSC_TYPE_RTC:
            __this->recover_time_us = BT_SLEEP_RECOVER_TIME_US;
            p33_tx_1byte(P3_PMU_CON1, PD_CON1_INIT_RTCL);
            p33_tx_1byte(P3_LP_TMR0_CLK, PD_CON1_INIT_RTCL);
            break;
        case OSC_TYPE_LRC:
            __this->recover_time_us = LRC_SLEEP_RECOVER_TIME_US + __this->hw.osc_delay_us;
            p33_tx_1byte(P3_PMU_CON1, PD_CON1_INIT_LRC_32K);
            p33_tx_1byte(P3_LP_TMR0_CLK, PD_CON1_INIT_LRC_32K);
            __hw_lrc_init();
            break;
        }
    if (__this->hw.osc_type != OSC_TYPE_RTC) {
        p33_tx_1byte(P3_OSL_CON, 0);
    }

    u8 check_ze_flash_id(void);
    if (check_ze_flash_id()) {
        __this->recover_time_us += 2000;
    }

    p33_tx_1byte(P3_PMU_CON2, PD_CON2_INIT);

    __this->recover_time_us += __this->hw.dcdc_delay_us;

    __this->Tcke = __tcnt_us(__this->recover_time_us);      ///恢复时间6000L;// ms
    __this->Tprp = __tcnt_us(SLEEP_SAVE_TIME_US);       ///保存时间
    __this->recover_total_time = __this->recover_time_us + RESET_HW_TIME_US + RESET_RESERVE_TIME_US;
    log_info("Prepare : %d(us) / Recover %d(us) / %d(us)", __tus_cnt(__this->Tprp), __tus_cnt(__this->Tcke),
        __this->recover_time_us);

    p33_tx_1byte(P3_LP_PRP0, __this->Tprp >> 8);
    p33_tx_1byte(P3_LP_PRP1, __this->Tprp >> 0);
    log_info("Low power Tprp : 0x%x / Tcke : 0x%x", __this->Tprp, __this->Tcke);

    __this->reserve_time = __this->Tprp + __this->Tcke + __tcnt_us(HW_RESERVE_TIME_US);
    __this->reserve_time += __tcnt_us(RESET_RESERVE_TIME_US);
    __this->reserve_time += __tcnt_us(CPU_RESERVE_TIME_US);
    log_info("Low power Reserve time: 0x%x (tick) / %d(us)", __this->reserve_time, __tus_cnt(__this->reserve_time));

    p33_tx_1byte(P3_WLDO12_AUTO,   pd_wldo12_auto_init);

    FLASH_KEEP(0);

    /* normal模式wvddio调到最低档, 低功耗之前调到配置挡位 */
    ASSERT(param->vddiow_lev >= VDDIOW_VOL_24V, "VDDIOW too low, please set upper than 2.4V");
    u8 vddiom_lev = (param->vddiom_lev >= vddiow2m_table[param->vddiow_lev]) ? param->vddiom_lev :
        vddiow2m_table[param->vddiow_lev];
    __this->hw.vddiom_lev = vddiom_lev;
    __this->hw.vddiow_lev = param->vddiow_lev;
    VDDIOW_VOL_SEL(VDDIOW_VOL_24V);
    VDDIOM_VOL_SEL(vddiom_lev);

    log_info("power VDDIO : %s / %s", vddiom_string[GET_VDDIOM_VOL()], vddiow_string[GET_VDDIOW_VOL()]);

    RC_250k_EN(1);        // rc250k en
    P33_CON_SET(P3_ANA_KEEP, 0, 6, 0);
    VDDOK_DIS(1);
    SYS_VDD_KEEP(0);

    WLDO06_EN(0);
    NV_RAM_POWER_GATE(0);
    VDC13_DESHOT_EN(1);
    VDC13_LOAD_EN(0);

    PDIO_KEEP(1);
}

static void __hw_power_off_enter(void)
{
}

static void __hw_power_down_enter(void)
{
}

/* ----------------------------------------------------------- */

/*
 *                   HW Abstact Layer
 */
#define LOW_POWER_OFF_ENABLE        0
void poweroff_pre(void);

static void low_power_enter(void)
{
    // disable LRC
    __hw_lrc_enable(0);

#if LOW_POWER_OFF_ENABLE
    poweroff_pre();
#endif

    if (__this->hw.powerdown_enter) {
        __this->hw.powerdown_enter(1);
    }

    __regs_push(__this->hw.regs, REGS_NUM);

    if (__this->hw.powerdown_enter) {
        __this->hw.powerdown_enter(2);
    }

    __hw_pdown_enter();
}

static void low_power_exit(u32 usec)
{
    __hw_pdown_exit();

    __regs_pop(__this->hw.regs, REGS_NUM);

    if (__this->hw.powerdown_exit) {
        __this->hw.powerdown_exit(usec);
    }

    // restart LRC
    __hw_lrc_enable(1);
}

void __low_power_resume(u32 usec)
{
    struct power *p, *n;

    __this->pending = 0;

    list_for_each_entry_safe(p, n, &__this->group0.request_head, entry) {
        if (p->ops && p->ops->resume) {
            p->ops->resume(p->priv, usec);
        }
    }

    list_for_each_entry_safe(p, n, &__this->group0.request_head, entry) {
        if (p->ops && p->ops->resume_post) {
            p->ops->resume_post(p->priv, usec);
        }
    }
}

static void __low_power_suspend(struct list_head *request_head, u32 usec)
{
    struct power *p;

    list_for_each_entry(p, request_head, entry) {
        if (p->ops && p->ops->suspend_probe) {
            p->ops->suspend_probe(p->priv);
        }
    }
    list_for_each_entry(p, request_head, entry) {
        if (p->ops && p->ops->suspend_post) {
            p->ops->suspend_post(p->priv, usec);
        }
    }
}

void __low_power_sys_resume(u32 usec)
{
    struct power *p, *n;

    __this->pending = 0;
    list_for_each_entry_safe(p, n, &__this->group1.request_head, entry) {
        if (p->ops && p->ops->resume) {
            p->ops->resume(p->priv, usec);
        }
    }
}

static void __power_on(u32 usec)
{
    struct power *p, *n;

    __this->pending = 0;
    list_for_each_entry_safe(p, n, &__this->group0.request_head, entry) {
        if (p->ops) {
            p->ops->on(p->priv);
        }
    }
}

/* ----------------------------------------------------------- */

// diable flash power  (code must run in RAM!!!)
// recovery flash power (code must run in RAM!!!)
AT_VOLATILE_RAM_CODE
void low_power_on(void)
{
    __this->pending = 0;
}

void low_power_request(void)
{
    if (__this->lock) {
        __this->pending = 1;
    } else {
    }
}

void *low_power_get(void *priv, const struct low_power_operation *ops)
{
    struct power *power;

    power = nv_memory_malloc(sizeof(*power));
    if (!power) {
        ASSERT(power != NULL);
        return NULL;
    }

    power->ops = ops;
    power->priv = priv;

    list_add_tail(&power->entry, &__this->group0.request_head);

    return power;
}

void low_power_put(void *priv)
{
    struct power *power = (struct power *)priv;

    list_del(&power->entry);

    nv_memory_free(power);
}

static void nv_timer0_process(u32 timeout, u8 mode)
{
    bool running;
    u32 tick;

    if (mode) {
        running = __hw_nv_timer_is_runnig(0);
        if (running) {
            tick = NV_TIMER0_GET_TIME();

            if ((__this->recover_time_us + RESET_HW_TIME_US + RESET_RESERVE_TIME_US < __tus_cnt(tick)) && running) {
                log_char('R');
                timeout = NV_TIMER0_GET_PASS_TIME() + __this->recover_time_us + RESET_RESERVE_TIME_US;
                __hw_nv_timer0_reset_time(timeout);
                timeout = NV_TIMER0_GET_PREIOD();
            }
            while (1) {
                u8 reg = p33_rx_1byte(P3_LP_TMR0_CON);
                if (reg & BIT(7)) {
                    log_char(']');
                    LP_IO_DEBUG_1(A, 7);
                    p33_or_1byte(P3_LP_TMR0_CON, BIT(6));
                    LP_IO_DEBUG_0(A, 7);
                    break;
                }
                if (reg & BIT(5)) {
                    LP_IO_DEBUG_1(A, 7);
                    p33_or_1byte(P3_LP_TMR0_CON, BIT(4));
                    __low_power_resume(timeout);
                    LP_IO_DEBUG_0(A, 7);
                    log_char('[');
                }
            }
        }
    } else {
        running = __hw_nv_timer_is_runnig(0);
        if (running) {
            timeout = NV_TIMER0_GET_PASS_TIME();
            __hw_nv_timer0_disable();
        }
    }

    __low_power_sys_resume(timeout);
}

static void nv_timer1_process(u32 timeout)
{
    bool running;

    running = __hw_nv_timer_is_runnig(1);
    if (running) {
        timeout = NV_TIMER1_GET_PASS_TIME();
        __hw_nv_timer1_disable();
    }

    __low_power_sys_resume(timeout);
}

void nvtimer_debug(void)
{
    log_info("0-PRD : %d", (NV_TIMER0_GET_PREIOD()));
    log_info("0-RCV : %d", __tus_cnt(NV_TIMER0_GET_TIME()));

    log_info("-1-PRD : %d", (NV_TIMER1_GET_PREIOD()));
    log_info("-1-RCV : %d", __tus_cnt(NV_TIMER1_GET_TIME()));
}

static void low_power_schedule(void)
{
    u32 wdt_timeout;
    u32 timeout = -1;

    if ((__this->config & SLEEP_EN) == 0) {
        return;
    }

    if (__this->hw.osc_type == OSC_TYPE_LRC) {
        if (__lrc_trace_ready() == FALSE) {
            return;
        }
    }

    if (__this->user_nv_timer_en) {
        hw_nv_timer1_reset_time(__this->nv_timer_interval * 1000);
    }

    OS_ENTER_CRITICAL();

    // 由于LRC 不稳原因，动态切换晶振
    low_power_change_osc_type();

    // 获取系统最少空闲时间
    u32 b = low_power_group_query(&__this->group1.request_head);
    /* log_info("b timeout %d", b); */

    // 获取蓝牙最少空闲时间
    u32 a = low_power_group_query(&__this->group0.request_head);

    // 获取最少空闲时间
    timeout = MIN(a, b);
    log_debug("timeout:%d", timeout);
    if (timeout == 0) {
        OS_EXIT_CRITICAL();
        return;
    }

    // 休眠时间不能大于看门狗复位时间的一半
    wdt_timeout = 1000 * wdt_get_time() / 2;
    if (wdt_timeout && (timeout > wdt_timeout)) {
        timeout = wdt_timeout;
    }

    if (__hw_nv_timer_is_runnig(0)) {
        __hw_nv_timer0_dump();
        ASSERT(0, "timer0 is running");
    }

    LP_IO_DEBUG_1(A, 6);
    // 若蓝牙活跃，则由蓝牙基带负责触发本次低功耗计数器（硬件保证时间不丢失，完整补偿）
    if (list_empty(&__this->group0.request_head) == 0) {
        // HW kick start mode
        if (JL_P33->PMU_CON & BIT(6)) {
            log_info("a-PRD : %d", (NV_TIMER0_GET_PREIOD()));
            log_info("a-RCV : %d", __tus_cnt(NV_TIMER0_GET_TIME()));
            __hw_nv_timer0_dump();
        }

        // 空闲时间设置到低功耗计数器，前提：时间满足一次保存和恢复时间
        if (__hw_nv_timer0_set_time(timeout) == FALSE) {
            p33_tx_1byte(P3_PMU_CON4, 0);
            OS_EXIT_CRITICAL();
            return;
        }

        __hw_nv_timer0_hw_kick_start();

        if (JL_P33->PMU_CON & BIT(6)) {
            log_info("b-PRD : %d", (NV_TIMER0_GET_PREIOD()));
            log_info("b-RCV : %d", __tus_cnt(NV_TIMER0_GET_TIME()));
            __hw_nv_timer0_dump();
        }

        timeout = NV_TIMER0_GET_PREIOD();
    } else {
        // 若无高精度硬件活跃，则由CPU负责触发本次低功耗计数器（CPU 保证时间和tick timer 不丢失，完整补偿）
        // SW kick start mode
        if (__hw_nv_timer0_set_time(timeout) == FALSE) {
            OS_EXIT_CRITICAL();
            return;
        }

        if (timeout != -2) {
            __hw_nv_timer0_sw_kick_start();
        }

        timeout = NV_TIMER0_GET_PREIOD();
    }

    // 挂起蓝牙基带成员（低功耗保护）
    __low_power_suspend(&__this->group0.request_head, timeout);

    // 挂起系统(低功耗保护)
    __low_power_suspend(&__this->group1.request_head, timeout);

    // 进入低功耗流程，适当关闭寄存器，用户特殊保存回调，电源切换
    low_power_enter();

    // 低功耗计数器计数并启动低功耗流程
    low_power_system_down();

    // 退出低功耗流程，恢复寄存器，用回特殊恢复回调，电源切换，时钟切换
    low_power_exit(timeout);

    if (__this->fatal_error) {
        log_info("c-PRD : %d", (NV_TIMER0_GET_PREIOD()));
        log_info("c-RCV : %d", __tus_cnt(NV_TIMER0_GET_TIME()));
        __hw_nv_timer0_dump();
    }
    ASSERT(__this->fatal_error == 0, "Recover Time Fatal Error = 0x%x", __this->fatal_error);

    DEBUG_SINGAL_LP_TIMER(0);

    // Query is wakeup
    u8 mode = (list_empty(&__this->group0.request_head) == 0) ? 1 : 0;

    // 检查是否由于唤醒源触发
    nv_timer0_process(timeout, mode);

    LP_IO_DEBUG_0(A, 6);

    OS_EXIT_CRITICAL();
}

void low_power_sys_request(void *priv)
{
    low_power_schedule();
}

void *low_power_sys_get(void *priv, const struct low_power_operation *ops)
{
    struct power *power;

    power = nv_memory_malloc(sizeof(*power));
    if (!power) {
        ASSERT(power != NULL, "sys get power error");
        return NULL;
    }
    power->ops = ops;
    power->priv = priv;

    list_add_tail(&power->entry, &__this->group1.request_head);

    return power;
}

u8 low_power_sys_is_idle(void)
{
    const struct lp_target *p;
    u8 idle = 1;

    list_for_each_lp_target(p) {
        if (p->is_idle() == 0) {
            log_debug("Name : %s busy", p->name);
        }
        idle &= p->is_idle();
    }
    return idle;
}

void low_power_sys_put(void *priv)
{
    struct power *power = (struct power *)priv;

    list_del(&power->entry);

    nv_memory_free(power);
}
/* ----------------------------------------------------------- */

u8 power_is_poweroff_probe(void)
{
    return 0;
}

AT_NON_VOLATILE_RAM_CODE
void power_set_proweroff(void)
{
}

u8 power_is_poweroff_post(void)
{
    return 0;
}

u8 __attribute__((weak)) poweroff_entry_cbfun(void)
{
    return 0;
}

void set_softoff_wakeup_time_ms(u32 ums)
{
    u32 fre_hz;
#if SOFTOFF_KEEP_LRC
    fre_hz = __get_lrc_hz();
#else
    fre_hz = 250000;
#endif

    __this->softoff_wakeup_t = ((u64)ums * (u64)((u64)fre_hz / (u64)64)) / 1000;
}

void set_softoff_wakeup_time_sec(u32 sec)
{
    u32 fre_hz;
#if SOFTOFF_KEEP_LRC
    fre_hz = __get_lrc_hz();
#else
    fre_hz = 250000;
#endif

    if (sec >= (0xffffffff / (fre_hz / 64))) {
        __this->softoff_wakeup_t =    (0xffffffff / (fre_hz / 64));
    } else {
        __this->softoff_wakeup_t = sec * (fre_hz / 64) ;
    }
}

AT_VOLATILE_RAM_CODE
void power_set_soft_poweroff(void)
{
    poweroff_entry_cbfun();

    static u8 soft_power_off_cnt = 0;
    soft_power_off_cnt = 0;

    log_info("--Enter Power off(Soft)");

    OS_ENTER_CRITICAL();

    __hw_enter_soft_poweroff();
    // 关机前判断充电唤醒边沿是否正确（可能休眠前有抖动导致唤醒源设置错误,LDOIN负载弱问题）
    if (charge_check_wakeup_is_set_ok() == FALSE) {
        P33_TX_NBIT(P3_PR_PWR, BIT(4), 1);//reset
    }

    while (1) {
        close_all_analog();

        /* usb_sie_close_all();//20191125， wuxu */

        close_flash();

        p33_tx_1byte(P3_ANA_LAT, 0xff);

        JL_SPI0->CON = 0;
        JL_SPI1->CON = 0;
        JL_SPI2->CON = 0;
        JL_SD0->CON0 = 0;
        JL_SD0->CON1 = 0;
        JL_SD0->CON2 = 0;
        JL_SD1->CON0 = 0;
        JL_SD1->CON1 = 0;
        JL_SD1->CON2 = 0;
        JL_IIC->CON0 = 0;
        JL_IIC->CON1 = 0;
        JL_PLED->CON0 = 0;

        p33_tx_1byte(P3_ANA_LAT, 0xff);

        p33_tx_1byte(P3_WKUP_CPND, 0xff);
        // Close power gate
        LP_KST;

        JL_CLOCK->PWR_CON |= BIT(2);

        soft_power_off_cnt++;
        // 休眠失败,有唤醒源唤醒,P33复位
        if (soft_power_off_cnt > 100) {
            P33_TX_NBIT(P3_PR_PWR, BIT(4), 1);
        }
    }
    // close all analog
}

extern void sys_voltage_reset(u8 pwr_mode);
void power_set_mode(u8 mode)
{
    static cur_mode = 0xff;

    if (cur_mode == mode) {
        return;
    }

    if ((mode == PWR_DCDC15) && IS_CHARGE_EN()) {
        return;
    }

    cur_mode = mode;

    if (cur_mode == PWR_DCDC15) {
        dcdc13_on(1000 + __this->hw.dcdc_delay_us);

    } else if (cur_mode == PWR_LDO15) {
        ldo13_on(1);
    }

    __this->mode = cur_mode;

    sys_voltage_reset(__this->mode);
}

void power_keep_dacvdd_en(u8 en)
{
    __this->keep_dacvdd = en;
}

void power_set_idle(void)
{
}

void power_set_callback(u8 mode, void (*powerdown_enter)(u8 step), void (*powerdown_exit)(u32),
    void (*soft_poweroff_enter)(void))
{
    __this->hw.powerdown_enter  = powerdown_enter;
    __this->hw.powerdown_exit   = powerdown_exit;
    __this->hw.soft_poweroff_enter  = soft_poweroff_enter;
}

void poweroff_recover(void)
{
}

static u8 cpu_soft_reset;

void reset_source_dump(void)
{
    u32 sfr;

    sfr = JL_RST->SRC;
    log_info("--Reset Source : 0x%x", sfr);

    if (sfr & BIT(0)) {
        log_info("P33 RESET");
    }
    if (sfr & BIT(1)) {
        log_info("DVDD POR");
    }
    if (sfr & BIT(5)) {
        log_info("SOFTR");
    }
    if (sfr & BIT(6)) {
        log_info("PLPM");
    }
}

static u8 p3_pmu_con1_flag = 0;
u8 power_return_flag()
{
    return (p3_pmu_con1_flag & BIT(7));
}

// 默认用printf打印，方便跟进复位源
u8 power_reset_source_dump(void)
{
    u8 sfr;

    cpu_soft_reset = 0;

    sfr = p33_rx_1byte(P3_PMU_CON1);
    p3_pmu_con1_flag = sfr;
    if ((sfr & BIT(7)) == 0) {
        printf("power return");
        return 0;
    }
    p33_or_1byte(P3_PMU_CON1, BIT(6));

    sfr = p33_rx_1byte(P3_RST_SRC);

    printf("--P3 Reset Source : 0x%x", sfr);

    if (sfr & BIT(0)) {
        printf("VDDIO POR");
    }
    if (sfr & BIT(1)) {
        printf("VDDIO LVD");
    }
    if (sfr & BIT(2)) {
        printf("WDT");
    }
    if (sfr & BIT(3)) {
        printf("VCM");
    }
    if (sfr & BIT(4)) {
        printf("PPINR");
    }
    if (sfr & BIT(5)) {
        printf("SYS RESET");
    }
    if (sfr & BIT(6)) {
        printf("SOFT RESET");
        cpu_soft_reset = 1;
    }
    return sfr;
}

AT_VOLATILE_RAM_CODE
void p33_soft_reset(void)
{
    P33_CON_SET(P3_PR_PWR, 4, 1, 1);
}

int cpu_reset_by_soft(void)
{
    return cpu_soft_reset;
}

void vPortSysSleepInit(void);
void timer1_sleep_init(void);

static void lrc_debug_out(void);

void power_set_wvdd(u8 level)
{
    __this->hw.pd_wdvdd_lev = level;
}

void nv_timer_interval_reset(u32 interval)
{
    __this->nv_timer_interval = interval;
}

__attribute__((weak)) u32 alm_trans_for_sec(void)
{
}

__attribute__((weak)) void rtc_save_time_vm(void)
{
}

__attribute__((weak)) void rtc_save_sum_nsec_vm(void)
{
}

__attribute__((weak)) u8 get_alarm_ctrl(void)
{
    return 0;
}

__attribute__((weak)) void vir_rtc_trim(void)
{
}

void softoff_keep_vir_rtc(void)
{
    u32 alm_sec = alm_trans_for_sec();
    u64 tmp64 = 0;
    u64 r;
    u8 set_alm_flag = 0;
    if (get_alarm_ctrl() && (alm_sec != 0) && ((alm_sec <= __this->vir_rtc_trim_time) ||
        __this->vir_rtc_trim_time == 0)) {
        tmp64 = (alm_sec - 5) * 1000000;
        set_alm_flag = 1;
    } else {
        tmp64 = (u64)__this->vir_rtc_trim_time * 1000000;
    }
    tmp64 = tmp64 * (__get_lrc_hz());
    r = tmp64 % 1000000L;
    tmp64 = tmp64 / 1000000L;

    if (r) {
        tmp64 += 1;
    }

    p33_tx_1byte(P3_LP_RSC10, (u8)(tmp64 >> 24));
    p33_tx_1byte(P3_LP_RSC11, (u8)(tmp64 >> 16));
    p33_tx_1byte(P3_LP_RSC12, (u8)(tmp64 >> 8));
    p33_tx_1byte(P3_LP_RSC13, (u8)(tmp64 >> 0));

    if (set_alm_flag) {
        tmp64 += 32000;
        p33_tx_1byte(P3_LP_PRD10, (u8)(tmp64 >> 24));
        p33_tx_1byte(P3_LP_PRD11, (u8)(tmp64 >> 16));
        p33_tx_1byte(P3_LP_PRD12, (u8)(tmp64 >> 8));
        p33_tx_1byte(P3_LP_PRD13, (u8)(tmp64 >> 0));
    } else {
        p33_tx_1byte(P3_LP_PRD10, (u8)(0xff));
        p33_tx_1byte(P3_LP_PRD11, (u8)(0xff));
        p33_tx_1byte(P3_LP_PRD12, (u8)(0xff));
        p33_tx_1byte(P3_LP_PRD13, (u8)(0xff));
    }

    rtc_save_time_vm();
    rtc_save_sum_nsec_vm();

    p33_tx_1byte(P3_LP_TMR1_CON, 0x57);
    SFR(JL_P33->PMU_CON, 3, 1, 1);

    p33_tx_1byte(P3_PMU_CON4, PD_CON4_INIT_TMR1_RTC);
    p33_tx_1byte(P3_PMU_CON1, (0x04));

    LP_KST;
}

void vir_rtc_get_actual_time(u64 *actual_time)
{
    u32 lp_timer1_pass_cnt;
    u16 addr;

    addr = P3_LP_CNTRD0;
    p33_cs_h(addr);

    p33_buf((P33_OR << 5) | (u8)((addr & 0x300) >> 8));               // or    //adr 3
    p33_buf((u8)(addr & 0xff));       // wr    //adr 3
    p33_buf(PD_LP1_CNT_READ);
    p33_buf(0);
    lp_timer1_pass_cnt = p33_buf(0) << 24;
    lp_timer1_pass_cnt |= p33_buf(0) << 16;
    lp_timer1_pass_cnt |= p33_buf(0) << 8;
    lp_timer1_pass_cnt |= p33_buf(0);

    p33_cs_l;

    log_info("lp_timer_pass_cnt = %d\n", lp_timer1_pass_cnt);
    u64 y;
    y = ((u64)lp_timer1_pass_cnt) * 1000000L;
    y = y / (__this->hw.osc_hz);

    u32 lrc_hz = __get_lrc_hz();
    *actual_time = (u64)((u64)y * 1000 * __this->hw.osc_hz / lrc_hz); // ns
}

static u8 vir_alm_wakeup_flag = 0;

u8 get_vir_alm_wakeup_flag(void)
{
    return vir_alm_wakeup_flag;
}

static void set_vir_alm_wakeup_flag()
{
    u8 lp_tmr1_con = p33_rx_1byte(P3_LP_TMR1_CON);
    u32 tmp32 = 0;
    if ((lp_tmr1_con & BIT(0)) && (lp_tmr1_con & BIT(5))) {
        tmp32 |= p33_rx_1byte(P3_LP_PRD10) << 24;
        tmp32 |= p33_rx_1byte(P3_LP_PRD11) << 16;
        tmp32 |= p33_rx_1byte(P3_LP_PRD12) << 8;
        tmp32 |= p33_rx_1byte(P3_LP_PRD13);

        if (tmp32 != 0xFFFFFFFF) {
            vir_alm_wakeup_flag = 1;
        } else {
            vir_alm_wakeup_flag = 0;
        }
    }
}

void power_init(const struct low_power_param *param)
{
    u16 addr;

    // disabel lp_timer0
    p33_tx_1byte(P3_LP_TMR0_CON, BIT(4) | BIT(6));
    __this->softoff_wakeup_t = 0;

    __this->config = param->config;

    __this->rtc_clk = param->rtc_clk;

    __this->hw.default_osc_type = param->osc_type;

    __this->hw.osc_delay_us     = param->osc_delay_us;

    __this->hw.dcdc_delay_us    = param->dcdc_delay_us;

    __this->vddio_keep = param->vddio_keep;

    __this->nv_timer_interval = param->nv_timer_interval;

    __this->user_nv_timer_en = param->user_nv_timer_en;

    __this->vir_rtc_trim_time = param->vir_rtc_trim_time;

    log_info("VDDIO KEEP : %d", __this->vddio_keep);

    __this->vdc13_keep = param->vdc13_keep;
    log_info("VDC13 KEEP : %d", __this->vdc13_keep);

    /* Digital Voltage set */
    __this->hw.pd_wdvdd_lev     = WLDO_LEVEL_066V;

    __this->lvd_keep = param->lvd_keep;

    __hw_power_init(param);

    // Group 0 for bluetooth module
    __hw_nv_timer0_init();

    // Group 1 for system module
    if (param->virtual_rtc || param->user_nv_timer_en) {
        if ((param->osc_type == OSC_TYPE_LRC) && (param->vir_rtc_trim_time)) {
            set_vir_alm_wakeup_flag();
            vir_rtc_trim();
        }
        rtc_hw_nv_timer1_init(__this->hw.osc_type, __this->hw.osc_hz, param->nv_timer_interval);
    } else {
        __hw_nv_timer1_init();
    }

    if (__this->config & SLEEP_EN) {
        vPortSysSleepInit();
        timer1_sleep_init();
        if (param->virtual_rtc || param->user_nv_timer_en) {
            rtc_nv_timer1_sleep_init();
        }
    }
}

void low_power_reset_osc_type(u8 type)
{
    __this->hw.osc_type = type;
}

u8 low_power_get_default_osc_type(void)
{
    return __this->hw.default_osc_type;
}

void low_power_inc_osc_delay_us(u32 delay)
{
}

u8 low_power_get_osc_type(void)
{
    return __this->hw.osc_type;
}

static void __hw_btosc_time_set(u32 osc_hz)
{
    __hw_set_osc_hz(osc_hz, 0);

    __this->hw.osc_hz >>= 6;   // default div 64

    p33_tx_1byte(P3_PMU_CON0, PD_CON0_INIT);

    __this->recover_time_us = BT_SLEEP_RECOVER_TIME_US + __this->hw.dcdc_delay_us;

    u8 check_ze_flash_id(void);
    if (check_ze_flash_id()) {
        __this->recover_time_us += 2000;
    }

    p33_tx_1byte(P3_LP_TMR0_CLK, PD_CON1_INIT_BT);

    p33_tx_1byte(P3_PMU_CON2, PD_CON2_INIT);

    __this->Tcke = __tcnt_us(__this->recover_time_us);      ///恢复时间6000L;// ms
    __this->reserve_time = __this->Tprp + __this->Tcke + __tcnt_us(HW_RESERVE_TIME_US);
    __this->reserve_time += __tcnt_us(RESET_RESERVE_TIME_US);
    __this->reserve_time += __tcnt_us(CPU_RESERVE_TIME_US);
}

static void __hw_lrc_time_set(u8 reset, u32 osc_hz)
{
    __hw_set_osc_hz(osc_hz, 1);

    if (reset) {
        p33_tx_1byte(P3_PMU_CON0, PD_CON0_INIT);

        __this->recover_time_us = LRC_SLEEP_RECOVER_TIME_US + __this->hw.osc_delay_us + __this->hw.dcdc_delay_us;

        u8 check_ze_flash_id(void);
        if (check_ze_flash_id()) {
            __this->recover_time_us += 2000;
        }

        p33_tx_1byte(P3_LP_TMR0_CLK,  PD_CON1_INIT_LRC_32K);

        p33_tx_1byte(P3_PMU_CON2, PD_CON2_INIT);
    }

    __this->Tcke = __tcnt_us(__this->recover_time_us);      ///恢复时间6000L;// ms
    __this->reserve_time = __this->Tprp + __this->Tcke + __tcnt_us(HW_RESERVE_TIME_US);
    __this->reserve_time += __tcnt_us(RESET_RESERVE_TIME_US);
    __this->reserve_time += __tcnt_us(CPU_RESERVE_TIME_US);
}

static void low_power_change_osc_type(void)
{
    struct low_power_param param;

    if (is_lcd_on()) {
        /* lcd时钟为Lrc，低功耗要一直使用lrc */
        __this->hw.osc_type = OSC_TYPE_LRC;
    }

    if (__this->hw.osc_type == OSC_TYPE_BT_OSC) {

        if (__this->hw.last_osc_type == __this->hw.osc_type) {
            return;
        }

        __hw_btosc_time_set(__this->hw.btosc_hz);
    }
    if (__this->hw.osc_type == OSC_TYPE_LRC) {

        // if osc type not change then only update Hz
        if (__this->hw.last_osc_type == __this->hw.osc_type) {

            // update Frequency(Hz) parameters because LRC trace
            __hw_lrc_time_set(0, __get_lrc_hz());
            return;
        }

        __hw_lrc_time_set(1, __get_lrc_hz());
    }

    __this->hw.last_osc_type = __this->hw.osc_type;

    log_debug("%s - %08d", (__this->hw.osc_type == OSC_TYPE_BT_OSC) ? "BT OSC" : "LRC", __this->hw.osc_hz);
}

AT_VOLATILE_RAM_CODE
void save_spi_port(void)
{
    p33_tx_1byte(P3_SOFT_FLAG, ((spi_get_port() == 0) ? SOFT_FLAG_BOOT_SPI_PORTD_A : SOFT_FLAG_BOOT_SPI_PORTD_B));
}

void power_reset_close(void)
{
    P33_CON_SET(P3_PINR_CON, 0, 8, 0);
}

void lvd_extern_wakeup_enable(void)
{
    P33_VLVD_EN(0);
    P33_VLVD_PS(1);
    VLVD_RST_EN(0);
    VLVD_WKUP_EN(1);
    P33_VLVD_EN(1);
    P33_VLVD_OE(1);
}

/* ---------------------------------------------------------------------------- */
/**@brief    蓝牙模式变量初始化
   @param    en     0: 关闭长按复位    1：开启长按复位
   @param    level  0: 低电平有效      1：高电平有效
   @param    sec    0:1s   1:2s    2:4s      3:8s
   @return   无
   @note
*/
/* ---------------------------------------------------------------------------- */
void power_reset_pinr(u8 en, u8 level, u8 sec)
{
    P33_CON_SET(P3_PINR_CON, 0, 1, en);
    P33_CON_SET(P3_PINR_CON, 2, 1, level);
    P33_CON_SET(P3_PINR_CON, 4, 2, sec);
}

/* ----------------------------------------------------------- */

AT_VOLATILE_RAM_CODE
void latch_reset(void)
{
    p33_tx_1byte(P3_ANA_LAT, 0x1);

    power_set_mode(PWR_LDO15);

    __hw_enter_soft_poweroff();

    while (1) {
        close_all_analog();
        close_flash();
        p33_tx_1byte(P3_ANA_LAT, 0xff);

        JL_SPI0->CON = 0;
        JL_SPI1->CON = 0;
        JL_SPI2->CON = 0;
        JL_SD0->CON0 = 0;
        JL_SD0->CON1 = 0;
        JL_SD0->CON2 = 0;
        JL_SD1->CON0 = 0;
        JL_SD1->CON1 = 0;
        JL_SD1->CON2 = 0;
        JL_IIC->CON0 = 0;
        JL_IIC->CON1 = 0;
        JL_PLED->CON0 = 0;

        p33_tx_1byte(P3_ANA_LAT, 0xff);

        p33_tx_1byte(P3_WKUP_CPND, 0xff);

        // reset
        JL_CLOCK->PWR_CON |= (1 << 4);
    }
}

